/*
    2021-4-11 此函数 实现字符设备驱动的创建

*/


#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/sysctl.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/ratelimit.h>

#include <asm/current.h>
#include "led_opr.h"
//确定主设备号，也可以让内核自动分配
#define LED_NUM   2

static int major = 0; //主设备号
static struct  class *led_class;// 设备节点
struct led_operations* p_led_opr;


//实现对应的drv_open  drv_read drw_wirter 等函数

static ssize_t  read_led_driver (struct file * file, char __user * buf, size_t size, loff_t *offset) //app read 的时候会调用到这个函数
{
    //nt err;
    printk("%s %s line %d \n",__FILE__,__FUNCTION__,__LINE__); //打印调试信息 ，文件名， 函数，行号
 
    return 0;
}
static ssize_t  write_led_driver (struct file * file, const char __user * buf, size_t size, loff_t * offset)    //APP  write 的时候会调用到这个函数
{
    int err;
    char status;
    struct inode *inode = file_inode(file);  //获得节点信息
    int minor = iminor(inode);   //获得次设备号  
    printk("%s %s line %d \n",__FILE__,__FUNCTION__,__LINE__);//打印调试信息 ，文件名， 函数，行号
    err = copy_from_user(&status,buf,1); //得到 app 发送的状态信息

   /*根据次设备号  和status 控制LED*/
    p_led_opr->ctl(minor,status);


    return 0;
}
static int open_led_driver (struct inode * node, struct file * file)  //app  open 调用这个函数
 {
     int minor = iminor(node);  //获取次设备号
     printk("%s %s line %d \n",__FILE__,__FUNCTION__,__LINE__);//打印调试信息 ，文件名， 函数，行号
     /*根据次设备号 初始化LED*/
     p_led_opr->init(minor);
     return  0;
}
static int release_led_driver (struct inode * node, struct file * file)  
{
    printk("%s %s line %d \n",__FILE__,__FUNCTION__,__LINE__);//打印调试信息 ，文件名， 函数，行号
     return  0;
}


//定义file_operations结构体
static struct file_operations led_drv = 
{
    .owner		= THIS_MODULE, //linux 中的一种特殊的初始化方式   指定初始化
   	.open		= open_led_driver,
	.read		= read_led_driver,
	.release	= release_led_driver,
	.write		= write_led_driver,

};
//把file_operations 结构体告诉内核 register_chrdev


// 谁来注册驱动程序？ 通过入口函数  在执行insmod 命令的时候会去调用 这个入口函数
static int __init led_drv_init(void)
{
    int err;
    int i;
    printk("%s %s line %d \n",__FILE__,__FUNCTION__,__LINE__);//打印调试信息 ，文件名， 函数，行号

    /*
    static inline int register_chrdev(unsigned int major, const char *name,
				  const struct file_operations *fops)
    */
    major = register_chrdev(0,"led",&led_drv);  //调用此函数内核 会创建一个 /dev/led 设备出来
    led_class = class_create(THIS_MODULE,"led_class");  //创建设备节点
    err = PTR_ERR(&led_class); //类型转换
    if(IS_ERR(led_class)) //判断设备节点是否创建成功
        {
            printk("%s %s line %d \n",__FILE__,__FUNCTION__,__LINE__);//打印调试信息 ，文件名， 函数，行号
            /*
            static inline void unregister_chrdev(unsigned int major, const char *name)
                    {
                    __unregister_chrdev(major, 0, 256, name);
                    }
           */
            unregister_chrdev(major,"led");
            return -1;
        }    
        for(i = 0; i < LED_NUM ;i++ )  //循环创建 设备节点
        device_create(led_class,NULL,MKDEV(major,i),NULL,"my_led%d",i); //创建设备   /dev/led

        p_led_opr = get_board_led_oper();
        return  0;
}
//卸载驱动程序时，出口函数会调用 unreagister_chardev函数 来卸载当前驱动程序
/*
static void __exit
srm_env_exit(void)
{
	remove_proc_subtree(BASE_DIR, NULL);
	printk(KERN_INFO "%s: unloaded successfully\n", NAME);
}
*/
static void __exit led_drv_exit(void)
{
    int i ;
     printk("%s %s line %d \n",__FILE__,__FUNCTION__,__LINE__);//打印调试信息 ，文件名， 函数，行号
     for(i=0 ;i< LED_NUM ;i++)
     device_destroy(led_class,MKDEV(major,i));  //删除设备节点

     class_destroy(led_class);//删除设备类 
     unregister_chrdev(major,"led");//注销设备
}


//提供设备信息，自动创建设备节点, class_create  device_create
module_init(led_drv_init);  //申明入口
module_exit(led_drv_exit);//申明出口

MODULE_LICENSE("GPL");

